package org.tloss.cluster;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringReader;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

public class ClusterServerFilter implements Filter {
	JAXBContext jaxbContext = null;
	Unmarshaller jaxbUnmarshaller = null;

	public void destroy() {

	}

	public String getFullURL(HttpServletRequest request, ClusterInfo info) {
		StringBuffer requestURL = new StringBuffer();
		requestURL.append(info.getScheme()).append("://")
				.append(info.getServer());
		if (info.getPort() != 80 && info.getPort() != 443) {
			requestURL.append(":").append(info.getPort());
		}

		String servletPath = request.getServletPath();
		String pathInfo = request.getPathInfo();
		if (info.getContextPath() != null && !"/".equals(info.getContextPath())) {
			requestURL.append(info.getContextPath());
		}
		if (servletPath != null) {
			requestURL.append(servletPath);
		}
		if (pathInfo != null) {
			requestURL.append(pathInfo);
		}
		String queryString = request.getQueryString();

		if (queryString == null) {
			return requestURL.toString();
		} else {
			return requestURL.append('?').append(queryString).toString();
		}
	}

	public boolean forward(ServletRequest req, ServletResponse resp,
			FilterChain filterChain, ClusterInfo clusterInfo)
			throws IOException, ServletException {
		HttpServletRequest httpServletRequest = (HttpServletRequest) req;
		HttpSession session = httpServletRequest.getSession(true);
		System.setProperty("http.keepAlive", "false");

		try {

			PrintWriter printWriter = resp.getWriter();
			String calURL = getFullURL((HttpServletRequest) req, clusterInfo);
			if (session != null
					&& session.getAttribute("JSESSIONID" + "_"
							+ clusterInfo.getName()) != null) {
				calURL = calURL
						+ ";jsessionid="
						+ session.getAttribute("JSESSIONID" + "_"
								+ clusterInfo.getName());
			}
			URL url = new URL(calURL);
			HttpURLConnection connection = (HttpURLConnection) url
					.openConnection();
			connection.setRequestProperty("Connection", "close");
			connection.setRequestMethod("GET");
			connection.setRequestProperty(Constants.SERVER_REQUEST,
					Constants.SERVER_REQUEST_VALUE);
			connection.setRequestProperty(Constants.SERVER_CLUSTER_NAME,
					clusterInfo.getName());
			List addSession = (List) session
					.getAttribute(Constants.ADD_SESSION);
			if (addSession != null) {
				String tmp;
				String[] tmpArr;
				SessionMeta obj = null;
				for (int i = 0; i < addSession.size(); i++) {
					tmp = (String) addSession.get(i);

					if (tmp != null && !tmp.equals("")) {
						try {
							obj = (SessionMeta) jaxbUnmarshaller
									.unmarshal(new StringReader(tmp));
							if (clusterInfo.getName().equals(
									obj.getClusterName())) {
								addSession.remove(i);
								i--;
							} else {
								connection.addRequestProperty(
										Constants.ADD_SESSION, tmp);
							}
						} catch (JAXBException e) {

						}

					}
				}
			}
			if (session != null
					&& session.getAttribute("JSESSIONID" + "_"
							+ clusterInfo.getName()) != null) {
				connection.setRequestProperty(
						"Cookie",
						"JSESSIONID="
								+ session.getAttribute("JSESSIONID" + "_"
										+ clusterInfo.getName()));

			}

			connection.setUseCaches(false);
			connection.setDoInput(true);
			connection.setDoOutput(true);
			int responseCode = connection.getResponseCode();
			if (responseCode == HttpURLConnection.HTTP_OK) {

				int i = 0;
				boolean stop = false;
				String headerName = null;
				String headerValue = null;
				if (session != null) {
					session.setAttribute("sessionid", session.getId());
					printWriter.println("sessionid=" + session.getId()
							+ "<br/>");
					Enumeration enumeration = session.getAttributeNames();
					while (enumeration.hasMoreElements()) {
						String tmp = (String) enumeration.nextElement();
						printWriter.println("" + tmp + ":="
								+ session.getAttribute(tmp) + "<br/>");

					}
				}

				addSession = (List) session.getAttribute(Constants.ADD_SESSION);
				if (addSession == null) {
					addSession = new ArrayList();
					session.setAttribute(Constants.ADD_SESSION, addSession);
				}
				printWriter
						.println("==========================================<br/>");
				while (!stop) {
					headerName = connection.getHeaderFieldKey(i);
					headerValue = connection.getHeaderField(i);
					i++;
					if (headerName == null && headerValue == null) {
						stop = true;
					} else {
						printWriter.println("" + headerName + "=" + headerValue
								+ "<br/>");
						if (Constants.ADD_SESSION.equals(headerName)) {
							addSession.add(headerValue);
						}
					}
					if ("set-cookie".equalsIgnoreCase(headerName)) {
						List cookies = HttpCookie.parse(headerValue);
						for (int j = 0; j < cookies.size(); j++) {
							HttpCookie cookie = (HttpCookie) cookies.get(j);
							if ("JSESSIONID".equals(cookie.getName())) {
								session.setAttribute("JSESSIONID" + "_"
										+ clusterInfo.getName(),
										cookie.getValue());
							}
						}
					}
					if (Constants.REMOTE_SESSION_ID.equals(headerName)) {
						session.setAttribute(
								"JSESSIONID" + "_" + clusterInfo.getName(),
								headerValue);
					}
				}

				printWriter
						.println("==========================================<br/>");
				// OK
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(connection.getInputStream()));
				String line;

				while ((line = reader.readLine()) != null) {
					printWriter.println(line);
				}
				reader.close();
				printWriter.flush();
			} else {
				// Server returned HTTP error code.
				HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
				httpServletResponse.sendError(responseCode);
			}
		} catch (java.net.SocketTimeoutException e) {
			return false;
		}
		return true;
	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain filterChain) throws IOException, ServletException {

		ClusterInfo clusterInfo = (ClusterInfo) clusterInfors.get(index
				% clusterInfors.size());
		index++;
		if (!forward(req, resp, filterChain, clusterInfo)) {
			// forward ko thanh cong
		}

	}

	List clusterInfors = new ArrayList();
	int index = 0;

	public void init(FilterConfig filterConfig) throws ServletException {
		try {
			jaxbContext = JAXBContext.newInstance(SessionMeta.class);
			jaxbUnmarshaller = jaxbContext.createUnmarshaller();
		} catch (JAXBException e) {
			throw new ServletException(e);
		}
		String clusterGroup = filterConfig.getInitParameter("cluster-group");
		String[] clusters = clusterGroup.split(",");
		for (int i = 0; i < clusters.length; i++) {
			String config = filterConfig.getInitParameter("cluster."
					+ clusters[i] + ".config");
			ClusterInfo clusterInfo = new ClusterInfo();
			clusterInfors.add(clusterInfo);
			clusterInfo.setName(clusters[i]);
			String[] configs = config.split(",");
			clusterInfo.setScheme(configs[0]);
			clusterInfo.setServer(configs[1]);
			clusterInfo.setPort(Integer.parseInt(configs[2]));
			clusterInfo.setContextPath(configs[3]);
		}

	}

}
